<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
</head>

<body>
  <canvas id="webgl" width="500" height="500" style="background-color: #0d72da"></canvas>
  <!-- 顶点着色器源码 -->
  <script id="vertexShader" type="x-shader/x-vertex">
  //attribute声明vec4类型变量apos
  attribute vec4 apos;
  // attribute声明顶点颜色变量
  attribute vec4 a_color;
  //顶点法向量变量
  attribute vec4 a_normal;
  // uniform声明平行光颜色变量
  uniform vec3 u_lightColor;
  //平行光方向
  uniform vec3 u_lightDirection;
  //varying声明顶点颜色插值后变量
  varying vec4 v_color;
  /**uniform声明旋转矩阵变量mx、my**/
  uniform mat4 mx;//绕x轴旋转矩阵
  uniform mat4 my;//绕y轴旋转矩阵
  void main() {
    //两个旋转矩阵、顶点齐次坐标连乘
    gl_Position = mx*my*apos;
    // 顶点法向量进行矩阵变换，然后归一化
    vec3 normal = normalize((mx*my*a_normal).xyz);
    // 计算平行光方向向量和顶点法向量的点积
    float dot = max(dot(u_lightDirection, normal), 0.0);
    // 计算反射后的颜色
    vec3 reflectedLight = u_lightColor * a_color.rgb * dot;
    //颜色插值计算
    v_color = vec4(reflectedLight, a_color.a);
  }

</script>
  <!-- 片元着色器源码 -->
  <script id="fragmentShader" type="x-shader/x-fragment">
  // 所有float类型数据的精度是lowp
  precision lowp float;
  // 接收顶点着色器中v_color数据
  varying vec4 v_color;
  void main() {
    // 插值后颜色数据赋值给对应的片元
    gl_FragColor = v_color;
  }

</script>

  <script>
    //通过getElementById()方法获取canvas画布
    var canvas = document.getElementById('webgl');
    //通过方法getContext()获取WebGL上下文
    var gl = canvas.getContext('webgl');

    //顶点着色器源码
    var vertexShaderSource = document.getElementById('vertexShader').innerText;

    //片元着色器源码
    var fragShaderSource = document.getElementById('fragmentShader').innerText;
    //调用函数initShader(),初始化着色器,返回program对象
    var program = initShader(gl, vertexShaderSource, fragShaderSource);
    /**
     * 从program对象获取相关的变量
     * attribute变量声明的方法使用getAttribLocation()方法
     * uniform变量声明的方法使用getAttribLocation()方法
     **/
    var aposLocation = gl.getAttribLocation(program, 'apos');
    var a_color = gl.getAttribLocation(program, 'a_color');
    var a_normal = gl.getAttribLocation(program, 'a_normal');
    var u_lightColor = gl.getUniformLocation(program, 'u_lightColor');
    var u_lightDirection = gl.getUniformLocation(program, 'u_lightDirection');
    /**从program对象获得旋转矩阵变量mx、my地址**/
    var mx = gl.getUniformLocation(program, 'mx');
    var my = gl.getUniformLocation(program, 'my');

    /**
     * 给平行光传入颜色和方向数据，RGB(1,1,1),单位向量(x,y,z)
     **/
    gl.uniform3f(u_lightColor, 1.0, 1.0, 1.0);
    //保证向量(x,y,z)的长度为1，即单位向量
    var x = 1 / Math.sqrt(15), y = 2 / Math.sqrt(15), z = 3 / Math.sqrt(15);
    gl.uniform3f(u_lightDirection, x, y, -z);

    /**
     创建顶点位置数据数组data,Javascript中小数点前面的0可以省略
     **/
    var data = new Float32Array([
      .5, .5, .5, -.5, .5, .5, -.5, -.5, .5, .5, .5, .5, -.5, -.5, .5, .5, -.5, .5,      //面1
      .5, .5, .5, .5, -.5, .5, .5, -.5, -.5, .5, .5, .5, .5, -.5, -.5, .5, .5, -.5,      //面2
      .5, .5, .5, .5, .5, -.5, -.5, .5, -.5, .5, .5, .5, -.5, .5, -.5, -.5, .5, .5,      //面3
      -.5, .5, .5, -.5, .5, -.5, -.5, -.5, -.5, -.5, .5, .5, -.5, -.5, -.5, -.5, -.5, .5,//面4
      -.5, -.5, -.5, .5, -.5, -.5, .5, -.5, .5, -.5, -.5, -.5, .5, -.5, .5, -.5, -.5, .5,//面5
      .5, -.5, -.5, -.5, -.5, -.5, -.5, .5, -.5, .5, -.5, -.5, -.5, .5, -.5, .5, .5, -.5 //面6
    ]);
    /**
     创建顶点颜色数组colorData
     **/
    var colorData = new Float32Array([
      1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,//红色——面1
      1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,//红色——面2
      1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,//红色——面3
      1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,//红色——面4
      1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,//红色——面5
      1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0 //红色——面6
    ]);
    /**
     *顶点法向量数组normalData
     **/
    var normalData = new Float32Array([
      0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,//z轴正方向——面1
      1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,//x轴正方向——面2
      0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0,//y轴正方向——面3
      -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0,//x轴负方向——面4
      0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0,//y轴负方向——面5
      0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1//z轴负方向——面6
    ]);
    /**
     创建缓冲区normalBuffer，传入顶点法向量数据normalData
     **/
    var normalBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, normalData, gl.STATIC_DRAW);
    gl.vertexAttribPointer(a_normal, 3, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(a_normal);
    /**
     创建缓冲区colorBuffer，传入顶点颜色数据colorData
     **/
    var colorBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, colorData, gl.STATIC_DRAW);
    gl.vertexAttribPointer(a_color, 3, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(a_color);
    /**
     创建缓冲区buffer，传入顶点位置数据data
     **/
    var buffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
    gl.vertexAttribPointer(aposLocation, 3, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(aposLocation);


    /**执行绘制之前，一定要开启深度测试，以免颜色混乱**/
    gl.enable(gl.DEPTH_TEST);
    /**绕x轴旋转45度**/
    var mxArr = new Float32Array([
      1, 0, 0, 0,
      0, Math.cos(Math.PI / 4), -Math.sin(Math.PI / 4), 0,
      0, Math.sin(Math.PI / 4), Math.cos(Math.PI / 4), 0,
      0, 0, 0, 1
    ]);
    //把数据mxArr传递给着色器旋转矩阵变量mx
    gl.uniformMatrix4fv(mx, false, mxArr);
    /**
     * 定义绘制函数draw()，定时更新旋转矩阵数据，并调用WebGL绘制API
     ***/
    var angle = Math.PI / 4;//起始角度
    function draw() {
      // gl.clear(gl.COLOR_BUFFER_BIT);//清空画布上一帧图像
      /**
       * 立方体绕y轴旋转
       ***/
      angle += 0.01;//每次渲染角度递增，每次渲染不同的角度
      var sin = Math.sin(angle);//旋转角度正弦值
      var cos = Math.cos(angle);//旋转角度余弦值
      var myArr = new Float32Array([cos, 0, -sin, 0, 0, 1, 0, 0, sin, 0, cos, 0, 0, 0, 0, 1]);
      gl.uniformMatrix4fv(my, false, myArr);
      requestAnimationFrame(draw);
      /**执行绘制命令**/
      gl.drawArrays(gl.TRIANGLES, 0, 36);
    }
    draw();


    //声明初始化着色器函数
    function initShader(gl, vertexShaderSource, fragmentShaderSource) {
      var vertexShader = gl.createShader(gl.VERTEX_SHADER);
      var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
      gl.shaderSource(vertexShader, vertexShaderSource);
      gl.shaderSource(fragmentShader, fragmentShaderSource);
      gl.compileShader(vertexShader);
      gl.compileShader(fragmentShader);
      var program = gl.createProgram();
      gl.attachShader(program, vertexShader);
      gl.attachShader(program, fragmentShader);
      gl.linkProgram(program);
      gl.useProgram(program);
      return program;
    }
  </script>
</body>

</html>